home *** CD-ROM | disk | FTP | other *** search
- From: rsalz@pineapple.bbn.com
- Newsgroups: comp.sources.misc
- Subject: v02i044: emake - "pre-processor" for makefiles
- Message-ID: <7192@ncoast.UUCP>
- Date: 6 Feb 88 01:24:10 GMT
- Approved: allbery@ncoast.UUCP
-
- Comp.sources.misc: Volume 2, Issue 44
- Submitted-By: "Rich $alz" <rsalz@pineapple.bbn.COM>
- Archive-Name: emake
-
- [Cross-pollination? ;-) ++bsa]
-
- One easy way to have standard make appear to be more powerful, is to use
- m4 or the C preprocessor so you can define parameterized macros for things
- like simple programs, and the like. Our project used to do this, and I
- notice that the X software uses something called "imake" which does this
- too.
- We used to use shell scripts, then I wrote this. (Then we rewrote all our
- Makefiles so we don't need this at all.) It basically runs cpp over a
- file named Dmakefile to create something called MakeAuto, it then invokes
- make on that. If MakeAuto is more recent then Dmakefile, you save the cpp
- overhead (this is the big problem with imake). A -D option also forces a
- rebuild.
-
- This lets you do cute things like put #ifdef's in your Makefile for
- different unices. Nice. It doesn't really handle multi-line #define's as
- the X imake does, but you could probably slip that in by putting in a
- filter pass to translate @@ into \n.
-
- Known to work on BSD derivatives, should take five or so minutes to port
- to others (e.g., no sys/file.h, or using cc -E instead of /lib/cpp.)
-
- Hope others find it useful.
- /r$
-
- #! /bin/sh
- # This is a shell archive. Remove anything before this line, then unpack
- # it by saving it into a file and typing "sh file". To overwrite existing
- # files, type "sh file -c". You can also feed this as standard input via
- # unshar, or by typing "sh <file", e.g
- PATH=/bin:/usr/bin: ; export PATH
- if test -f 'emake.c' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'emake.c'\"
- else
- echo shar: Extracting \"'emake.c'\" \(5585 characters\)
- sed "s/^X//" >'emake.c' <<'END_OF_FILE'
- X/*
- X** EMAKE
- X** Run /lib/cpp over Dmakefile (if necessary), then call make.
- X** There is a prolog file, the Dmakefile, and the epilog file;
- X** see the List variable to set these.
- X**
- X** This creates a makefile called MakeAuto, so you don't have to
- X** spend all that silly time in cpp if the Dmakefile hasn't changed.
- X*/
- X#include <stdio.h>
- X#include <errno.h>
- X#include <sys/types.h>
- X#include <sys/stat.h>
- X#include <sys/file.h>
- X
- X#ifdef WAIT_UNION
- X#include <sys/wait.h>
- X#define WAITVALUE(W) ((W).w_retcode)
- Xtypedef union wait WAITER;
- X#else
- X#define WAITVALUE(W) ((W) >> 8)
- Xtypedef int WAITER;
- X#endif /* WAIT_UNION */
- X
- X
- X/*
- X** Handy shorthands.
- X*/
- X#define STDOUT 1
- X#define STDERR 2
- X#define WHITE(c) ((c) == ' ' || (c) == '\t')
- X#define WRITE(s) (void)write(STDERR, s, sizeof s - 1);
- X
- X
- X/*
- X** Datatype and variable to hold the list of CPP directives that we should
- X** pass through (make's comment character is '#', which clashes).
- X*/
- Xtypedef struct {
- X char Value[8];
- X int Length;
- X} ALIST;
- X
- XALIST Directives[] = {
- X { "include", 7 }, { "define", 6 }, { "ifndef", 6 },
- X { "ifdef", 5 }, { "undef", 5 }, { "endif", 5 },
- X { "else", 4 }, { "line", 4 }, { "if", 2},
- X { "", -1 }
- X};
- X
- X/* Other globals. */
- Xchar TempInput[] = "MakeIXXXXXX"; /* CPP input file */
- Xchar TempOutput[] = "MakeOXXXXXX"; /* CPP output file */
- Xchar DMAKEFILE[] = "Dmakefile"; /* Emake's makefile */
- Xchar MAKEFILE[] = "MakeAuto"; /* Generated makefile */
- Xchar *List[] = { /* Sources for emake */
- X "/usr/cronus/clib/dmakedefs", DMAKEFILE, "/usr/cronus/clib/dtargets", NULL
- X};
- X
- X/* Linked in later. */
- Xextern int errno;
- Xextern char *mktemp();
- Xextern char *malloc();
- X
- X
- X/*
- X** Print error message, clean up, and die.
- X*/
- Xvoid
- XQuit(s)
- X char *s;
- X{
- X perror(s);
- X#ifndef DEBUG
- X if ((unlink(TempInput) < 0 && errno != ENOENT)
- X || (unlink(TempOutput) < 0 && errno != ENOENT))
- X perror("Error in QUIT cleanup");
- X#endif /* DEBUG */
- X _exit(1);
- X}
- X
- X
- X/*
- X** Pre-process the input files, building the make control file.
- X*/
- Xvoid
- XPrepare(Cargv)
- X char **Cargv;
- X{
- X register ALIST *D;
- X register FILE *F;
- X register FILE *In;
- X register char *p;
- X register int i;
- X register int j;
- X WAITER W;
- X char **Name;
- X char buff[BUFSIZ];
- X
- X /* Create tempfile for CPP input. */
- X if ((F = fopen(TempInput, "w")) == NULL)
- X Quit(TempInput);
- X
- X /* Write each input file to the temporary output. */
- X for (Name = List; *Name; Name++) {
- X if ((In = fopen(*Name, "r")) == NULL)
- X Quit(*Name);
- X
- X /* Read input, eliding #foo lines if foo is not a cpp directive. */
- X while (fgets(buff, sizeof buff, In))
- X if (buff[0] != '#')
- X (void)fputs(buff, F);
- X else {
- X for (p = &buff[1]; *p && WHITE(*p); p++)
- X p++;
- X for (i = strlen(p), D = Directives; D->Length >= 0; D++)
- X if (i > D->Length
- X && strncmp(p, D->Value, D->Length) == 0
- X && WHITE(p[D->Length])) {
- X (void)fputs(buff, F);
- X break;
- X }
- X }
- X
- X (void)fclose(In);
- X }
- X (void)fclose(F);
- X
- X /* Create a file to hold the cpp output. */
- X i = open(TempOutput, O_WRONLY | O_TRUNC | O_CREAT, 0666);
- X
- X /* Call the pre-processor. */
- X if ((j = fork()) == 0) {
- X /* I tried to use dup() and dup2(), but they didn't work... */
- X if (close(STDOUT) < 0 || dup(i) != STDOUT)
- X perror("Error in CPP redirection");
- X execv(Cargv[0], Cargv);
- X perror(Cargv[0]);
- X _exit(1);
- X }
- X
- X /* Wait for it. */
- X while (wait(&W) != j)
- X ;
- X if (WAITVAL(W))
- X Quit("CPP failure");
- X
- X /* Copy cpp output to MAKEFILE, eliding all "#" lines. */
- X (void)close(i);
- X if ((In = fopen(TempOutput, "r")) == NULL
- X || (F = fopen(MAKEFILE, "w")) == NULL)
- X Quit("Scanning cpp output");
- X (void)fputs("## HANDS OFF THIS FILE--IT WAS AUTOMATICALLY CREATED!!\n", F);
- X while (fgets(buff, sizeof buff, In))
- X if (buff[0] != '#')
- X for (p = buff; *p && *p != '\n'; p++)
- X if (!WHITE(*p)) {
- X (void)fputs(buff, F);
- X break;
- X }
- X (void)fclose(In);
- X (void)fclose(F);
- X if (unlink(TempInput) < 0 || unlink(TempOutput) < 0)
- X perror("Error in cleaning up temp files");
- X}
- X
- X
- Xmain(ac, av)
- X int ac;
- X register char *av[];
- X{
- X register char **Margv;
- X register char **Cargv;
- X register char *p;
- X register int Mcount;
- X register int Ccount;
- X register int Force;
- X struct stat Sb1;
- X struct stat Sb2;
- X
- X /* Is it all there? */
- X if (stat(DMAKEFILE, &Sb1) < 0)
- X Quit("Required file Dmakefile is missing");
- X
- X /* Is Dmakefile newer than MakeFile? */
- X Force = stat(MAKEFILE, &Sb2) < 0 || Sb1.st_mtime >= Sb2.st_mtime;
- X
- X /* Build argument list stubs. */
- X Margv = (char **)malloc((unsigned int)(ac + 4) * sizeof (char *));
- X Margv[0] = "make";
- X Margv[1] = "-f";
- X Margv[2] = MAKEFILE;
- X Cargv = (char **)malloc((unsigned int)(ac + 3) * sizeof (char *));
- X Cargv[0] = "/lib/cpp";
- X Cargv[1] = "-I/usr/cronus/include";
- X
- X /* Create spool files. */
- X (void)mktemp(TempInput);
- X (void)mktemp(TempOutput);
- X
- X /* Scan arg list, moving "-Dxxx" to cpp, all other stuff to make. */
- X for (Mcount = 3, Ccount = 2; p = *++av; )
- X if (p[0] == '-' && p[1] == 'D') {
- X Force++;
- X Cargv[Ccount++] = p;
- X }
- X else
- X Margv[Mcount++] = p;
- X Cargv[Ccount++] = TempInput;
- X Cargv[Ccount] = NULL;
- X Margv[Mcount] = NULL;
- X
- X /* Rebuild MAKEFILE if necessary. */
- X if (Force) {
- X static char REBUILD[] = "Rebuilding...";
- X static char DONE[] = " done\n";
- X
- X WRITE(REBUILD);
- X Prepare(Cargv);
- X WRITE(DONE);
- X }
- X
- X /* Now have make do the real work. */
- X (void)execvp(Margv[0], Margv);
- X Quit(Margv[0]);
- X}
- END_OF_FILE
- if test 5585 -ne `wc -c <'emake.c'`; then
- echo shar: \"'emake.c'\" unpacked with wrong size!
- fi
- # end of 'emake.c'
- fi
- echo shar: End of shell archive.
- exit 0
-